/*
 Функциональное описание:

*/
`timescale 1ns / 10ps
//    ********************** НАЧАЛО МОДУЛЯ *********************************************************
module mod_rx_cmd
    (   input  logic            reset_n,
        input  logic            clk,

        // Сигналы формирования видеокадра
        input  logic            sof,        // Начало кадра
        input  logic            eof,        // Конец кадра
        
        input  logic            data_eth_wre,
        input  logic [7:0]      data_eth,
        
        output logic            tx_req,
        output logic [7:0]      tx_data,
        // Сигналы RS
        input  logic            rxd,
        input  logic            rts,
        output logic            txd,
        output  logic           cts,
        
        // Сигналы доступа к памяти параметров
        output logic [7:0]      param_data,
        output logic            param_wre,
        output logic [31:0]     param_addr,
        
        // Разрешение контрастирования
        output logic            staining_ena,
        
        // Запуск синхрогенератора
        output logic            start,
        
        // Управление считыванием данных видео
        input  logic [7:0]      image_data,
        output logic [31:0]     image_addr,

        output logic            image_save_ena,
        
        output logic [31:0]     time_cnt,
        output logic            dval_trig,
        
        output logic [15:0]     max_limit,
        output logic [15:0]     min_limit,
        
        input  logic [71:0]     version,
        
        output  logic            adc_param_set,
        output  logic [2:0]      adc_param_addr,
        output  logic [8:0]      adc_param_reg
        
    );
    
// ********************** МАКРОСЫ ******************************************************************
    // Описывает процесс модуля выделения фронтов сигнала. 
    // Так же описывает создание новых сигналов с именем <имя сигнала> + <_rise> (<_fall>, <_edge>)
    `define FRONT(clk, name)                    \
    logic name``_0;                             \
    logic name``_rise;                          \
    logic name``_fall;                          \
    logic name``_edge;                          \
    always_ff @(posedge clk)                    \
    begin                                       \
        name``_0 <= name;                       \
        name``_rise <= name & (~name``_0);      \
        name``_fall <= name``_0 & (~name);      \
        name``_edge <= name ^ name``_0;         \
    end
//    ********************* КОНСТАНТЫ **************************************************************

    
//    ********************* СОЗДАНИЕ И ОПИСАНИЕ ПЕРЕМЕННЫХ *****************************************
    enum logic [8:0]    {   WAIT_START_s,
                            CMD_DETECT_s,
                            RX_PARAM_s,
                            IMAGE_TX_s,
                            WAIT_BUSY_s,
                            WAIT_FRAME_BEGIN_s,
                            WAIT_FRAME_END_s,
                            RX_LIMIT_s,
                            TX_VER_s,
                            TX_VER_WAIT_BUSY_s,
                            RX_ADC_PARAM_s
                        } state_cmd_ctrl;
    
    logic [8:0][7:0] ver;
   
    logic               tx_busy;
    logic               data_rs_wre;
    logic               dval;

    logic               image_tx_ena;
    logic [7:0]         data_rs;

    logic [7:0]         data_latch;
    
    logic [31:0]        wdt_cnt;
    logic [31:0]        image_tx_timer;
    
    logic [3:0][7:0]    limits;
    logic [1:0][7:0]    adc_param;

    
//  ********************* БЛОК НЕПРЕРЫВНЫХ НАЗНАЧЕНИЙ ASSIGN ***************************************
    assign cts         = tx_busy;
    assign min_limit   = {limits[1], limits[0]};
    assign max_limit   = {limits[3], limits[2]};
    
    assign  adc_param_addr   = adc_param[1][6:4];
    assign adc_param_reg    = {adc_param[1][0], adc_param[0][7:0]};
    
    assign ver      = version;
    assign dval     = data_rs_wre | data_eth_wre;
//  ********************* ОПИСАНИЕ ПРОЦЕССОВ *******************************************************
    `FRONT (clk, dval)    

    
    always_ff @(posedge clk)       
    begin    
        time_cnt    <= time_cnt + 32'd1;
        
        if (dval_rise) begin
            time_cnt    <= 32'd0;
            dval_trig   <= ~dval_trig;
        end
        else;
        
        if (data_rs_wre)
            data_latch  <= data_rs;
        else if (data_eth_wre)
            data_latch  <= data_eth;
        else;
    end
    
    // Счетчик сторожевого таймера на случай зависания
    always_ff @(posedge clk)
    begin    
        wdt_cnt  <= wdt_cnt + 32'd1;
        if ((state_cmd_ctrl == WAIT_START_s) ||
            (state_cmd_ctrl == IMAGE_TX_s))
            wdt_cnt  <= 32'd0;
        else;
    end    
    
    
    always_ff @(posedge clk, negedge reset_n)       
    begin    
        if (!reset_n) begin
            state_cmd_ctrl  <= WAIT_START_s;
        end
        else begin
            start           <= 1'b0;
            tx_req          <= 1'b0;
            image_save_ena  <= 1'b0;
            param_wre       <= 1'b0;
            adc_param_set   <= 1'b0;
            if (wdt_cnt  >= 2*100_000_000) begin        // сторожевой таймер на случай зависания
                state_cmd_ctrl  <= WAIT_START_s;
            end
            else begin
                case (state_cmd_ctrl)
                    WAIT_START_s:          
                    begin
                        if ((dval_fall) && (data_latch == 8'h24)) begin
                            state_cmd_ctrl  <= CMD_DETECT_s;
                        end
                        else;
                    end
                    
                    CMD_DETECT_s:
                    begin
                        param_addr  <= 32'd0;
                        image_addr  <= 32'd0;
                        if (dval_fall) begin
                            case (data_latch[3:0])
                                4'd1:   begin
                                    state_cmd_ctrl  <= RX_PARAM_s;
                                end
                                
                                4'd2:   begin
                                    start           <= 1'b1;
                                    state_cmd_ctrl  <= WAIT_START_s;
                                end
                                
                                4'd3:   begin
                                    state_cmd_ctrl  <= WAIT_FRAME_BEGIN_s;
                                    // state_cmd_ctrl  <= IMAGE_TX_s;
                                end

                                4'd4:   begin
                                    staining_ena    <= 1'b1;
                                    state_cmd_ctrl  <= RX_LIMIT_s;
                                end
                                
                                4'd5:   begin
                                    staining_ena    <= 1'b0;
                                    state_cmd_ctrl  <= WAIT_START_s;
                                end
                                
                                4'd6:   begin
                                    state_cmd_ctrl  <= TX_VER_s;
                                end
                                
                                4'd7:   begin
                                    state_cmd_ctrl  <= RX_ADC_PARAM_s;
                                end                                
                                default;
                            endcase;
                        end
                        else;
                    end
                    
                    RX_LIMIT_s:
                    begin
                        if (dval_rise) begin
                            param_addr  <= param_addr + 32'd1;
                            limits[param_addr]    <= data_latch;
                        end
                        else;
                        
                        if (param_addr > 3)
                            state_cmd_ctrl  <= WAIT_START_s;
                        else;
                    end
                    
                    RX_PARAM_s: begin
                        if (dval_rise) begin
                            param_data  <= data_latch;
                            param_wre   <= 1'b1;
                        end
                        else if (dval_fall) begin
                            param_addr  <= param_addr + 32'd1;
                            if (param_addr > 1023)
                                state_cmd_ctrl  <= WAIT_START_s;
                            else;
                        end
                        else;
                    end
                        
                    WAIT_FRAME_BEGIN_s: begin
                        if (sof)
                            state_cmd_ctrl  <= WAIT_FRAME_END_s;
                        else;
                    end
                    
                    WAIT_FRAME_END_s: begin
                        image_save_ena    <= 1'b1;
                         image_tx_timer <= 0;
                        if (eof) begin
                            state_cmd_ctrl  <= IMAGE_TX_s;
                        end
                        else;
                    end
                    
                    IMAGE_TX_s: begin
                        // image_tx_ena    <= 1'b1;
                        if (!tx_busy) begin
                            image_tx_timer  <= image_tx_timer + 1;
                            if (image_tx_timer >= 20) begin
                                image_tx_timer  <= 0;
                                tx_req          <= 1'b1;
                                tx_data         <= image_data;
                                state_cmd_ctrl  <= WAIT_BUSY_s;
                            end
                            else;
                        end
                        else;
                    end
                    
                    WAIT_BUSY_s: begin
                        image_tx_ena    <= 1'b1;
                        if (tx_busy) begin
                            image_addr <= image_addr + 32'd1;
                            tx_req          <= 1'b0;
                            if (image_addr < (320 * 240 * 2))
                                state_cmd_ctrl  <= IMAGE_TX_s;
                            else
                                state_cmd_ctrl  <= WAIT_START_s;
                        end
                        else;                    
                    end 
                    
                    TX_VER_s: begin
                        if (!tx_busy) begin
                            image_tx_timer  <= image_tx_timer + 1;
                            if (image_tx_timer >= 20) begin
                                image_tx_timer  <= 0;
                                tx_req          <= 1'b1;
                                tx_data         <= ver[param_addr];
                                state_cmd_ctrl  <= TX_VER_WAIT_BUSY_s;
                            end
                            else;
                        end
                        else;
                    end
                    
                    TX_VER_WAIT_BUSY_s: begin
                        if (tx_busy) begin
                            param_addr  <= param_addr + 32'd1;
                            tx_req      <= 1'b0;
                            if (param_addr < 9)
                                state_cmd_ctrl  <= TX_VER_s;
                            else
                                state_cmd_ctrl  <= WAIT_START_s;
                        end
                        else;  
                    end
                    
                    RX_ADC_PARAM_s:
                    begin
                        if (dval_rise) begin
                            param_addr  <= param_addr + 32'd1;
                            adc_param[param_addr]    <= data_latch;
                        end
                        else;
                        
                        if (param_addr > 1) begin
                            adc_param_set   <= 1'b1;
                            state_cmd_ctrl  <= WAIT_START_s;
                        end
                        else;
                    end
                    default: ;
                endcase
            end
        end
    end    
//  ********************* ПОДКЛЮЧЕНИЕ МОДУЛЕЙ ******************************************************
    RS485_RECEIVER
    RS485_RECEIVER_inst
        (   .CLK        (clk),
            .RST        (!reset_n), 
            .n          (32'd858),
            
            .RX_IN      (rxd),//& (~rts)
            
            .D_VAL      (data_rs_wre),
            .D_OUT      (data_rs),
            .BIT_PARITY_ERR()
            
        );
        
    RS485_TRANSMITTER
    RS485_TRANSMITTER_inst
        (   .CLK        (clk),
            .RST        (!reset_n), 
            .n          (32'd858),

            .TX_OUT     (txd),
            
            .D_IN       (tx_data),
            .START_TXD  (tx_req),
            .BUSY       (tx_busy),
            .parity_ena (1'b0)
        );
    
endmodule
`undef FRONT